/*
**  File: AquaFuncts_pdbmr.c
**
**  This file is part of the AQUA program suite, which is being
**  developed in the NMR Spectroscopy Department, Bijvoet Center for 
**  Biomolecular Research, Utrecht. 
**
**
**  AUTHOR(S) : Ton Rullmann
**  VERSION   : 1.0
**  DATE      : Oct 8, 1996
**
**  This file contains routines for handling PDB 'mr' files.
**
**  Most header definitions are present in AquaFuncts_io.c
*/

/****************  function prototypes  ****************/

#include "AquaFuncts_pdbmr.h"

static void ReadHeader_pdbmr( FILE *fp, char *header, const size_t size );
static Status ReadAtomData_pdbmr( FILE *fp, char *atnam_entry, char *atnam_orig, 
                                  char *resnam, int *atnum, int *resnum, 
			          const size_t size, const int count );
static Status ReadCoordData_pdbmr( FILE *fp, float *x, float *y, float *z );

/* BEGIN_EXTH */

/****************  ReadFil_cv_pdbmr  ****************/
/*
**  Read a PDB 'mr' file in the ReadFil_cv way.
*/

Status ReadFil_cv_pdbmr( FILE *fp, const Filtyp type, const int atom_count,
                         int *fil_struc_num, int *nerr )
{
    int      count = 0;
    char     *a, *b, *r;
    int      *ia, *ir;
    float    *px, *py, *pz;
    Status   stat;

/* allocate memory */

    Alloc_cv( atom_count );
        
/* read the file header - only for the first structure */

    if ( !*fil_struc_num )
	ReadHeader_pdbmr( fp, header, size_HEADER );
    
/* read the predetermined number of atoms */

    (*fil_struc_num)++;
    a = atonam;                            /* the external name (orig) */
    b = btonam;                            /* the internal name (entry) - see ToAqua_cv */
    r = resnam;
    ia = atono;
    ir = resno;
    px = x;
    py = y;
    pz = z;

    while ( ++count <= atom_count )
    {
        stat = ReadAtomData_pdbmr( fp, b, a, r, ia++, ir++, size_ID, count );
        if ( stat != status_ok )
	{
	    if ( stat == eof_err && count == 1 )
		return( end_of_data );
	    else
                goto err;
	}
        stat = ReadCoordData_pdbmr( fp, px++, py++, pz++ );
        if ( stat != status_ok )
            goto err;
        a += size_ID;
        b += size_ID;
        r += size_ID;

        if ( FlushLine( fp ) < 0 )
	{
	    stat = eof_err;
            goto err;
	}
    }
    return( status_ok );

err:
    *nerr = count-1;
    puts( " * Error or unexpected end of file" );
    return( stat );
}

/****************  AtomCount_pdbmr  ****************/
/*
**  Count the number of atoms per structure in a PDB 'mr' file.
*/

int AtomCount_pdbmr( FILE *fp )
{
    char     *fct_name = "AtomCount_pdbmr";
    Card     newl;
    char     *line;
    int      count = 0, num;
    Boolean  model = false;

    STATUS("starting");
    
    while ( ReadLine( newl, size_LINE, fp ) )
    {
	line = FirstNonBlank( newl );

        if ( EQn( line, "Raw", 3 ) )
/* if reading from "Raw file had ..." fails, then count explicitly */
	{
	    if ( sscanf( line, "Raw file had %i", &count ) == 1 )
		break;
	}
/* stop at second model */
	if ( EQn( line, "Start", 5 ) )
	{
	    if ( model )
		break;
	    else
		model = true;
	}
	if ( !model)
	    continue;
/* try reading atom number, and check vs. the counter */
	if ( sscanf( line, "%i", &num ) == 1 )
	    if ( num != ++count )
	    {
		puts( "non-consecutive atom numbers" );
		return( 0 );
	    }
    }

    STATUS("ending");
    return( count );
}

/* END_EXTH ----------------------- only private functions follow */

/****************  ReadHeader_pdbmr  ****************/
/*
**  Read header information from a PDB 'mr' file
**  Format assumed: 
**  - text header copied from lines beginning with: HEADER, COMPND, AUTHOR, 
**    REVDAT or REMARK
**    (only the first MAXHEADER of such lines are stored) 
**  - data are assumed to start after a line beginning with: END, 'Start of', 
**    'Entry' or 'Raw file' (leading whitespace is ignored).
**
**  After returning the file is positioned at the beginning of a new line.
*/

static void ReadHeader_pdbmr( FILE *fp, char *header, const size_t size )
{
    char  *fct_name = "ReadHeader_pdbmr";
    Card  newl;
    char  *h = header, *line;
    int   numh = 0;

    STATUS("starting");

    while ( ReadLine( newl, size_LINE, fp ) )
    {
	line = FirstNonBlank( newl );
	
        if ( EQn( line, "HEADER", 6 ) || EQn( line, "COMPND", 6 ) || 
             EQn( line, "AUTHOR", 6 ) || EQn( line, "REVDAT", 6 ) ||
	     EQn( line, "REMARK", 6 ) )
        {
            if ( numh++ < MAXHEADER )
            {
                strncpy( h, line, size );
                h += size;
            }
        }
        else if ( EQn( line, "END", 3 ) || EQn( line, "Start of", 8 ) ||
                  EQn( line, "Entry", 5 ) || EQn( line, "Raw file", 8 ) )
            break;
    }

    STATUS("ending");
}

/****************  ReadAtomData_pdbmr  ****************/
/*
**  Read atom information from one PDB 'mr' record
**  Format assumed: 
**
**  Entry H atom name              Orig H atom name
**    1    H    PCA   1           HN   PCA   1   0.710 -16.690  13.113
**
**  The routine finds and reads the first line starting with a number. 
**  This number is checked against the running atom count, i.e. it should 
**  be increasing in steps of 1. 
**  Premature end-of-file generates an error condition.
**
**  After reading the file is **not** flushed to the next line.
*/

static Status ReadAtomData_pdbmr( FILE *fp, char *atnam_entry, char *atnam_orig, 
                                  char *resnam, int *atnum, int *resnum, 
			          const size_t size, const int count )
{
    int       num;
    Card      line;
    Status    stat;
    
    while ( true )
    {
	if ( !ReadWord( line, size_LINE, false, fp ) )
	{                           /* input first word */
	    if ( feof( fp ) || ferror( fp ) )
		return( eof_err );
	    else
		continue;           /* skip empty line */
	}

        if ( EQn( line, "Raw", 3 ) || EQn( line, "Entry", 5 ) ||
	     EQn( line, "Start", 5 ) )
	{
	    if ( count != 1 )
		return( eof_err );  /* if not reading the first atom:
				    stop at header line */
	    else
	    {
		if ( FlushLine( fp ) < 0 )
		    return( eof_err );
		continue;           /* else: skip header line */
	    }
	}

	if ( sscanf( line, "%i", &num ) == 1 )
	{
	    if ( num != count )     /* try reading atom number,
	    		            and check vs. the counter */
	    {
		puts( "non-consecutive atom numbers" );
		stat = read_err;
		goto err;
	    }
	    else
		break;
	}
	else
	{                           /* disabled: stop at non-numeric first word */
/*
	    stat = read_err;
	    goto err;
*/
	    puts( "skipping unexpected line starting with:" );
	    puts( line );
	}

        if ( FlushLine( fp ) < 0 )
            return( eof_err );      /* stop if no more lines */
    }

    if ( !ReadWord( line, size_LINE, true, fp ) )
	return( read_err );
    CopyChars( atnam_entry, line, size );
    ReadWord( line, size_LINE, true, fp );
    ReadWord( line, size_LINE, true, fp );
    if ( !ReadWord( line, size_LINE, true, fp ) )
	return( read_err );
    CopyChars( atnam_orig, line, size );
    if ( !ReadWord( line, size_LINE, true, fp ) )
	return( read_err );
    CopyChars( resnam, line, size );
    if ( fscanf( fp, "%i", resnum ) < 1 )
    {
        puts( "Error in reading atom data from PDB 'mr' record" );
        return( read_err );
    }
    *atnum = num;

    return( status_ok );
err:
    printf( " * Last read: %s\n", line );
    return( stat );
}

/****************  ReadCoordData_pdbmr  ****************/
/*
**  Read coordinate information from one PDB 'mr' record
**  Format assumed: free format floats
**
**  After reading the file is **not** flushed to the next line.
**  Returns status flag.
*/

static Status ReadCoordData_pdbmr( FILE *fp, float *x, float *y, float *z )
{
    if ( fscanf( fp, " %f %f %f", x, y, z ) < 3 )
    {
        puts( " * Error in reading coordinate data from PDB 'mr' record" );
        return( read_err );
    }
    return( status_ok ); 
}
